package com.xlf.service.base.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageInfo;
import com.xlf.common.auth.util.SecurityUtils;
import com.xlf.common.core.exception.BizException;
import com.xlf.common.core.pojo.vo.UserVO;
import com.xlf.common.core.util.BeanCopyUtils;
import com.xlf.common.core.util.StringUtils;
import com.xlf.common.core.util.page.PageUtils;
import com.xlf.common.core.util.page.TableDataInfo;
import com.xlf.common.redis.service.RedisCache;
import com.xlf.pojo.base.ContestProblem;
import com.xlf.pojo.base.Judge;
import com.xlf.pojo.base.JudgeCase;
import com.xlf.pojo.base.Problem;
import com.xlf.pojo.constant.Constants;
import com.xlf.pojo.dto.RunResult;
import com.xlf.pojo.dto.TestJudgeReq;
import com.xlf.service.base.mapper.ContestProblemMapper;
import com.xlf.service.base.mapper.JudgeCaseMapper;
import com.xlf.service.base.mapper.ProblemMapper;
import com.xlf.service.base.pojo.dto.JudgeListReqDTO;
import com.xlf.service.base.pojo.dto.SubmitJudgeDTO;
import com.xlf.service.base.pojo.dto.TestJudgeDTO;
import com.xlf.service.base.pojo.vo.*;
import com.xlf.service.base.service.JudgeService;
import com.xlf.service.base.mapper.JudgeMapper;
import com.xlf.service.base.service.impl.validate.ContestValidator;
import com.xlf.service.base.service.impl.validate.JudgeValidator;
import com.xlf.service.system.service.impl.CommonUserVOService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageBuilder;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.SerializationUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.xlf.common.core.constant.MqConstant.Judge.*;
import static com.xlf.common.core.constant.RedisConst.TEST_JUDGE_LOCK;
import static com.xlf.common.core.util.page.PageUtils.getDataTable;
import static com.xlf.common.core.util.page.PageUtils.startPage;

/**
* @author 小新
* @description 针对表【judge】的数据库操作Service实现
* @createDate 2023-10-05 21:34:42
*/
@Service
@Slf4j
public class JudgeServiceImpl extends ServiceImpl<JudgeMapper, Judge>
    implements JudgeService{

    @Autowired
    JudgeValidator judgeValidator;

    @Resource
    RabbitTemplate rabbitTemplate;

    @Resource
    RedisCache redisCache;

    @Resource
    ProblemMapper problemMapper;

    @Resource
    JudgeMapper judgeMapper;

    @Resource
    JudgeCaseMapper judgeCaseMapper;



    @Autowired
    CommonUserVOService commonUserVOService;

    @Autowired
    ContestValidator contestValidator;

    @Resource
    ContestProblemMapper contestProblemMapper;


    @Override
    public String submitTestJudge(TestJudgeDTO testJudgeDto) {


        Long userId = (Long) SecurityUtils.getUserId();

        String uuid = UUID.randomUUID().toString();
        String testKey = TEST_JUDGE_LOCK  + userId.toString() + ":" + uuid;

        TestJudgeReq testJudgeReq = new TestJudgeReq();

        testJudgeReq.setMemoryLimit(256 * 1024)
                .setTimeLimit(1000 + 200)
                .setStackLimit(256 * 1024)
                .setCode(testJudgeDto.getCode())
                .setLanguage(testJudgeDto.getLanguage())
                .setTestCaseInput(testJudgeDto.getTestCaseInput())
                .setExpectedOutput(testJudgeDto.getExpectedOutput())
                .setUniqueKey(testKey);

        Optional.ofNullable(testJudgeDto.getPid())
                .map(item -> problemMapper.selectById(item))
                .ifPresent(item -> testJudgeReq
                .setMemoryLimit(item.getMemoryLimit())
                .setTimeLimit(item.getTimeLimit())
                .setStackLimit(item.getStackLimit())
                .setIsRemoveEndBlank(item.getIsRemoveEndBlank()));

        RunResult runResult = new RunResult();
        runResult.setStatus(Constants.Judge.STATUS_PENDING.getStatus());
        redisCache.setCacheObject(testKey,runResult,5, TimeUnit.MINUTES);

        // 将对象转换成字节数组
        byte[] bytes = SerializationUtils.serialize(testJudgeReq);

        Message message = MessageBuilder
                .withBody(bytes)
                .setPriority(Constants.JudgePriority.TEST.getPriority())
                .build();
        CorrelationData correlationData = new CorrelationData(); //关联数据
        correlationData.setId(uuid); //唯一标识
        rabbitTemplate.convertAndSend(JUDGE_EXCHANGE,JUDGE_TEST_KEY,message,correlationData);
        return uuid;
    }


    @Override
    public JudgeVO submitProblemJudge(SubmitJudgeDTO judgeDto) {

        //是否非比赛提交
        boolean submitType = judgeDto.getCid().equals(0L);

        Judge judge = Judge.builder()
                .pid(judgeDto.getPid())
                .userId((Long)SecurityUtils.getUserId())
                .cid(judgeDto.getCid())
                .submitTime(new Date())
                .status(Constants.Judge.STATUS_PENDING.getStatus())
                .share(submitType ? 1 : 0)
                .length(judgeDto.getCode().length())
                .code(judgeDto.getCode())
                .language(judgeDto.getLanguage())
                .build();

        //校验层
        judgeValidator.judgeLanguageValidate(judge);
        contestValidator.submitValidator(judge.getCid());

        //非比赛提交
        if(submitType){
            Problem problem = problemMapper.selectOne(new LambdaQueryWrapper<Problem>()
                    .select(Problem::getId,Problem::getProblemId)
                    .eq(Problem::getId, judgeDto.getPid())
                    .eq(Problem::getStatus, com.xlf.common.core.constant.Constants.NORMAL));
            Assert.notNull(problem,() -> new BizException("题目不存在!"));
            judge.setPCode(problem.getProblemId());

        }else { //比赛提交
            ContestProblem contestProblem = contestProblemMapper.selectOne(new LambdaQueryWrapper<ContestProblem>()
                    .select(ContestProblem::getPid, ContestProblem::getSeq)
                    .eq(ContestProblem::getPid, judgeDto.getPid())
                    .eq(ContestProblem::getCid,judge.getCid())
                    .eq(ContestProblem::getStatus, com.xlf.common.core.constant.Constants.NORMAL));
            Assert.notNull(contestProblem,() -> new BizException("题目不存在!"));
            judge.setPCode(contestProblem.getSeq());

        }

        if(!save(judge))
            throw new BizException("判题提交失败！");

        //确定优先级
        Integer priority = submitType ? Constants.JudgePriority.TRAIN.getPriority() :
                Constants.JudgePriority.CONTEST.getPriority();
        //丢信息队列
        String msg = judge.getId().toString();
        Message message = MessageBuilder
                .withBody(msg.getBytes())
                .setPriority(priority) //优先级
                .build();
        CorrelationData correlationData = new CorrelationData(); //关联数据
        correlationData.setId(judge.getId().toString()); //唯一标识
        rabbitTemplate.convertAndSend(JUDGE_EXCHANGE,JUDGE_PROBLEM_KEY,message,correlationData);
        return BeanCopyUtils.copyObject(judge,JudgeVO.class);
    }


    @Override
    public RunResult getTestJudgeResult(String testJudgeKey) {

        Long userId = (Long) SecurityUtils.getUserId();

        String testKey = TEST_JUDGE_LOCK  + userId.toString() + ":" + testJudgeKey;

        RunResult runResult = redisCache.getCacheObject(testKey);

        if(runResult == null){
            RunResult result = new RunResult();
            result.setStatus(Constants.Judge.STATUS_NOT_SUBMITTED.getStatus());
            return result;
        }
        return runResult;
    }


    @Override
    public JudgeResultVO getSubmission(Long judgeId) {

        Judge judge = getById(judgeId);
        if(StringUtils.isNull(judge))
            throw new BizException("判题结果不存在！");
        JudgeResultVO judgeResultVO = BeanCopyUtils.copyObject(judge, JudgeResultVO.class);

        return judgeResultVO;
    }


//    @Override
//    public List<JudgeVO> getJudgeList(JudgeListReqDTO judgeListReqDTO) {
//
//
//        contestValidator.queryValidator(judgeListReqDTO.getCid());
//
////        //比赛查询
////        if(!judgeListReqDTO.getCid().equals(0L)){
////
////        }
////        startPage();
//        lambdaQuery().select(Judge::getId,Judge::getUserId,Judge::getSubmitTime,Judge::getStatus,Judge::getShare,Judge::getTime
//        Judge::getMemory,Judge::getScore,Judge::getLength,Judge::getCode,Judge::getLanguage);
//        QueryWrapper<JudgeVO> wrapper = new QueryWrapper<>();
//        wrapper.eq(StringUtils.isNotEmpty(judgeListReqDTO.getLanguage()),"judge.language",judgeListReqDTO.getLanguage())
//                .eq(StringUtils.isNotNull(judgeListReqDTO.getStatus()),"judge.status",judgeListReqDTO.getStatus())
//                .eq("judge.cid",judgeListReqDTO.getCid())
//                .eq(StringUtils.isNotEmpty(judgeListReqDTO.getProblemCode()),"p.problem_id",judgeListReqDTO.getProblemCode())
//                .orderByDesc("judge.submit_time");
//
//        //自己的提交纪录
//        if(judgeListReqDTO.getMyOwn() == 1){
//            Long userId = (Long)SecurityUtils.getUserId();
//            wrapper.eq("user_id",userId);
//        }else {
//            if(StringUtils.isNotEmpty(judgeListReqDTO.getNickName())){
//                Set<Long> ids = commonUserVOService.getUserIdsByNickName(judgeListReqDTO.getNickName());
//                if(StringUtils.isEmpty(ids))
//                    return new ArrayList<>();
//                wrapper.in("user_id",ids);
//            }
//        }
//
//        List<JudgeVO> judgeVOList = judgeMapper.getJudgeVOList(wrapper);
//
//        if(StringUtils.isNotEmpty(judgeVOList)){
//
//            Set<String> userIds = judgeVOList.stream().map(item -> item.getUserId().toString()).collect(Collectors.toSet());
//            Map<Long, UserVO> userInfoMap = commonUserVOService.getUserInfoMap(userIds);
//                judgeVOList.forEach(item -> {
//                    item.setUserVO(userInfoMap.get(item.getUserId()));
//                });
//        }
//
//        return judgeVOList;
//    }
//

    @Override
    public TableDataInfo<JudgeVO> getJudgeList(JudgeListReqDTO judgeListReqDTO) {


        contestValidator.queryValidator(judgeListReqDTO.getCid());

        LambdaQueryChainWrapper<Judge> lambdaQueryChainWrapper = lambdaQuery()
                .select(Judge::getId, Judge::getPCode,Judge::getUserId, Judge::getSubmitTime,
                        Judge::getStatus, Judge::getShare, Judge::getTime,Judge::getPCode,
                Judge::getMemory, Judge::getScore, Judge::getLength, Judge::getCode, Judge::getLanguage);


        lambdaQueryChainWrapper.eq(StringUtils.isNotEmpty(judgeListReqDTO.getLanguage()),Judge::getLanguage,judgeListReqDTO.getLanguage())
                .eq(StringUtils.isNotNull(judgeListReqDTO.getStatus()),Judge::getStatus,judgeListReqDTO.getStatus())
                .eq(Judge::getCid,judgeListReqDTO.getCid())
                .eq(StringUtils.isNotNull(judgeListReqDTO.getProblemId()),Judge::getPid,judgeListReqDTO.getProblemId())
                .eq(StringUtils.isNotEmpty(judgeListReqDTO.getProblemCode()),Judge::getPCode,judgeListReqDTO.getProblemCode())
                .orderByDesc(Judge::getSubmitTime);

        //自己的提交纪录
        if(judgeListReqDTO.getMyOwn() == 1){
            Long userId = null;
            try {
                userId = (Long) SecurityUtils.getUserId();
            } catch (Exception e) {
                //未登录
                return PageUtils.getEmptyDataTable();
            }
            lambdaQueryChainWrapper.eq(Judge::getUserId,userId);
        }else {

            if(StringUtils.isNotEmpty(judgeListReqDTO.getNickName())){
                Set<Long> ids = commonUserVOService.getUserIdsByNickName(judgeListReqDTO.getNickName());
                if(StringUtils.isEmpty(ids))
                    return PageUtils.getEmptyDataTable();
                lambdaQueryChainWrapper.in(Judge::getUserId,ids);
            }
        }

        startPage();
        List<Judge> list = lambdaQueryChainWrapper.list();

        if(StringUtils.isEmpty(list))
            return PageUtils.getEmptyDataTable();

        List<JudgeVO> judgeVOList = BeanUtil.copyToList(list, JudgeVO.class);

        Set<String> userIds = judgeVOList.stream().map(item -> item.getUserId().toString()).collect(Collectors.toSet());
        Map<Long, UserVO> userInfoMap = commonUserVOService.getUserInfoMap(userIds);
        judgeVOList.forEach(item -> item.setUserVO(userInfoMap.get(item.getUserId())));

        return getDataTable(judgeVOList,((Page)list).getTotal());
    }

    @Override
    public JudgeDetailVO getJudge(Long id) {

        Judge judge = getById(id);

        Assert.notNull(judge,() -> new BizException("判题结果不存在！"));

        contestValidator.queryValidator(judge.getCid());

        //代码仅本人或超级管理员可见
        if(judge.getShare() == 0){
            try {
                Long userId = (Long)SecurityUtils.getUserId();
                if((!SecurityUtils.isAdmin(userId)) && (!userId.equals(judge.getUserId())))
                    judge.setCode("");
            } catch (Exception e) {
//                e.printStackTrace();
            }
        }

        LambdaQueryWrapper<Problem> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Problem::getId,judge.getPid())
                .eq(Problem::getStatus, com.xlf.common.core.constant.Constants.NORMAL)
                        .select(Problem::getTitle);

        Problem problem = problemMapper.selectOne(wrapper);

        JudgeDetailVO judgeDetailVO = new JudgeDetailVO();

        problem = Optional.ofNullable(problem).orElseGet(() -> Problem.builder().title("题目已被删除").build());

        judgeDetailVO.setProblemVO(JudgeDetailVO.ProblemVO.builder()
                .id(judge.getPid())
                .title(problem.getTitle()).build());

        LambdaQueryWrapper<JudgeCase> judgeCaseLambdaQueryWrapper = new LambdaQueryWrapper<>();
        judgeCaseLambdaQueryWrapper.eq(JudgeCase::getSubmitId,judge.getId())
                .orderByDesc(JudgeCase::getSeq)
                .select(JudgeCase::getId,JudgeCase::getTime,
                        JudgeCase::getCaseId,JudgeCase::getProblemId,
                        JudgeCase::getMemory,JudgeCase::getSeq,
                        JudgeCase::getStatus,JudgeCase::getScore);

        UserVO userInfo = commonUserVOService.getUserInfo(judge.getUserId());
        judgeDetailVO.setUserVO(userInfo);
        judgeDetailVO.setBaseInfo(judge);
        judgeDetailVO.setJudgeCaseList(judgeCaseMapper.selectList(judgeCaseLambdaQueryWrapper));

        return judgeDetailVO;
    }


    @Override
    public TableDataInfo<RanksVO> getRanks() {

        startPage();
        List<RanksVO> list = judgeMapper.getRanks();
        if (StringUtils.isNotEmpty(list)) {
            Map<Long, UserVO> userInfoMap = commonUserVOService
                    .getUserInfoMap(list.stream()
                    .filter(item -> item.getUserId()!=null)
                    .map(item -> item.getUserId().toString())
                    .collect(Collectors.toSet()));
            list.forEach(item -> item.setUserVO(userInfoMap.get(item.getUserId())));
        }

        return getDataTable(list,new PageInfo(list).getTotal());
    }


}




